package cn.edu.neu.lab603.math

import breeze.linalg._
import breeze.numerics._

/** 数据中心链路重要性评价指标。
  *
  * Created by yfwz100 on 2016/10/13.
  */
object DCRank {

  /** 获得虚拟机权重的分布。
    *
    * @param links 邻接矩阵
    * @param res   外部资源权重
    * @param alpha 对勾函数参数 alpha
    * @param beta  对勾函数参数 beta
    * @param d     学习率
    * @param sigma 收敛最大值
    * @return 权重
    */
  def apply(links: DenseMatrix[Double], res: DenseVector[Double] = null,
            alpha: Double = 0.01,
            beta: Double = 2,
            d: Double = 0.15,
            sigma: Double = 1e-10
           ): DenseVector[Double] = {
    solve(dcMatrix(links, alpha, beta), res, d, sigma)
  }

  protected[math] def solve(t: DenseMatrix[Double], res: DenseVector[Double],
                            d: Double,
                            sigma: Double
                           ): DenseVector[Double] = {
    val initDist = Option(res).map(_ :* res.size.toDouble).getOrElse(DenseVector.fill(t.rows, 1.0))
    var x = DenseVector.rand[Double](t.rows)
    var pre: DenseVector[Double] = null
    val lr = 1 - d
    val rres = Option(res).getOrElse(DenseVector.fill(t.rows, 0.0))
    val p = t(::, *).map { v =>
      val tmp = 0.8 * v + 0.2 * rres
      tmp / sum(tmp)
    }
    //    println(t)
    //    println(p)
    do {
      pre = x.copy
      x = lr * (p * x) + d * initDist
    } while (norm(pre - x) > sigma)
    x
  }

  protected[math] def dcMatrix(links: DenseMatrix[Double],
                               alpha: Double,
                               beta: Double
                              ): DenseMatrix[Double] = {
    val n = links.rows
    val result = DenseMatrix.zeros[Double](n, n)
    val uv = 1 / n
    val sv = 1 - uv * (n - 1)
    for (r <- 0 until n) {
      val l: Double = sum(links(::, r).map(v => if (v > 0) 1 else 0))
      val m: Double = n - l - 1
      val w: Double = 1 - pow(l / n, alpha) + pow(l / n, beta)
      for (c <- 0 until n) {
        if (r == c) {
          // 自己
          result.update(r, c, w * sv)
        } else if (links(r, c) > 0) {
          // 相连
          result.update(r, c, (1.0 - m * uv - w * sv) / l)
        } else {
          // 无关
          result.update(r, c, uv)
        }
      }
    }
    result.t
  }

}
